import { removeRule, rule } from '@/services/ant-design-pro/api';
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components';
import {
    FooterToolbar,
    PageContainer,
    ProDescriptions,
    ProTable,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl, useRequest, useSearchParams } from '@umijs/max';
import { Button, Drawer, Dropdown, Input, Menu, Modal, message } from 'antd';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Http } from '@/@nasty/Axios';
import { util } from '@/@nasty/Util';
import '@xyflow/react/dist/style.css';
import {
    Background,
    Controls,
    addEdge,
    MiniMap,
    Panel,
    ReactFlow,
    ReactFlowProvider,
    useEdgesState,
    useNodesState,
    useReactFlow,
    MarkerType
} from '@xyflow/react';
import { AddNodeModel, RejectNodeModel, UpdateNodeModel } from './view.model';
import { EndNode, GeneralNode, StartNode } from './common';

const initialNodes = [
    {
        id: 'Start',
        type: 'start',
        data: { label: '开始', candidateMode: 1, preUserIds: [], roleIds: [], departmentIds: [] },
        position: { x: 50, y: 50 },
    },
    {
        id: 'End',
        type: 'end',
        data: { label: '结束', candidateMode: 1, preUserIds: [], roleIds: [], departmentIds: [] },
        position: { x: 400, y: 50 },
    },
];

const emptyMenuItems = [
    { key: 'add', label: '新增节点' },
    { key: 'cancel', label: '取消' }
];

const nodeMenuItems = [
    { key: 'update', label: '编辑' },
    { key: 'reject', label: '驳回' },
    { key: 'delete', label: '删除' }
];

const nodeTypes = {
    "start": StartNode,
    "end": EndNode,
    "general": GeneralNode,
};

function getRejectEdge(source: string, target: string) {
    let data = {
        id: `reject_${source}_source_top_${target}_target_top`,
        markerEnd: {
            color: "#f96b64",
            type: "arrowclosed"
        },
        source: source,
        sourceHandle: `${source}_source_top`,
        style: {
            stroke: "#f96b64",
            strokeWidth: 2
        },
        target: target,
        targetHandle: `${target}_target_top`,
        animated: true,
        type: 'step',
        logicType: "reject",
    }

    return data;
}

function getGeneralEdge(source: string, target: string) {
    let data = {
        id: `xy-edge__${source}${source}_source_right-${target}${target}_target_left`,
        markerEnd: {
            color: "#7dd3fc",
            type: "arrowclosed"
        },
        source: source,
        sourceHandle: `${source}_source_right`,
        style: {
            stroke: "#7dd3fc",
            strokeWidth: 2
        },
        target: target,
        targetHandle: `${target}_target_left`,
        animated: true,
        type: 'step',
        logicType: "general",
    }

    return data;
}

function getFlowNode(v: any) {
    let type = "general";
    if (v.Code == "Start") type = "start";
    if (v.Code == "End") type = "end";

    let preUserIds = (v.PreUsers || []).map((t: any) => { return { label: t.Name, value: t.Id } });
    let roleIds = (v.Roles || []).map((t: any) => { return { label: t.Name, value: t.Id } });
    let departmentIds = (v.Departments || []).map((t: any) => { return { label: t.Name, value: t.Id } });

    let data = {
        sysId: v.Id,
        id: v.Code,
        type: type,
        data: { label: v.Name, candidateMode: v.CandidateMode, preUserIds, roleIds, departmentIds },
        position: { x: v.PositionX, y: v.PositionY },
    }

    return data;
}

function getFlowViewData(items: any[]) {
    let nodes: any[] = [];
    let edges: any[] = [];
    items.forEach((t: any) => {
        let node = getFlowNode(t);
        nodes.push(node);

        let pcodes = t.PreNodeCodes || [];
        pcodes.forEach((pcode: any) => {
            let gedge = getGeneralEdge(pcode, t.Code);
            edges.push(gedge);
        });

        if (t.RejectNodeCode) {
            let redge = getRejectEdge(t.Code, t.RejectNodeCode);
            edges.push(redge);
        }
    });


    return { nodes, edges };
}

async function SaveFlowConfigNodes(options: any) {
    return new Promise<any>((resolve, reject) => {
        Http.post("/Flow/FlowConfig/SaveFlowConfigNodes", { ...options })
            .then((data) => {
                if (data.IsSuccess !== true)
                    reject(data.Message);
                else
                    resolve(data);
            }).catch((e) => {
                reject(e)
            })
    });
}

const FlowConfigView: React.FC = () => {
    const containerRef = React.useRef(null);
    const addNodeRef = React.useRef(null);
    const updateNodeRef = React.useRef(null);
    const rejectNodeRef = React.useRef(null);
    const [messageApi, contextHolder] = message.useMessage();
    const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
    const [edges, setEdges, onEdgesChange] = useEdgesState([]);

    const [menuVisible, setMenuVisible] = useState(false);
    const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
    const [menuItems, setMenuItems] = useState<any[]>([]);
    const [currentNode, setCurrentNode] = useState<any>({});
    const [searchParams, setSearchParams] = useSearchParams();

    const [isPublish, setIsPublish] = useState(false);

    const intl = useIntl();

    const getFlowPosition = (event: any) => {
        const rect = containerRef.current?.getBoundingClientRect() || { left: 0, top: 0 };
        const relativeX = event.clientX - rect?.left || 0;
        const relativeY = event.clientY - rect?.top || 0;
        return { x: relativeX - 45, y: relativeY };
    }

    const onPaneContextMenu = useCallback((event: any) => {
        event.preventDefault();
        if (isPublish) return;
        let pos = getFlowPosition(event);
        setMenuPosition(pos);
        setMenuItems(emptyMenuItems);
        setMenuVisible(true);
    }, [isPublish]);

    const onNodeContextMenu = useCallback((event: any, node: any) => {
        event.preventDefault();
        if (isPublish) return;
        //开始、结束是特殊节点无法进行操作
        if (node.id == "Start" || node.id == "End") return;

        let pos = getFlowPosition(event);
        setMenuPosition(pos);
        setMenuItems(nodeMenuItems);
        setCurrentNode(node);
        setMenuVisible(true);
    }, [isPublish]);

    const handleMenuClick = (v: any) => {
        if (v.key == "cancel") setMenuVisible(false);
        if (v.key == 'add') addNodeRef.current?.open();
        if (v.key == 'delete') {
            setNodes(nodes.filter(item => item.id !== currentNode.id));
            setEdges(edges.filter((item: any) => item.source != currentNode.id && item.target != currentNode.id));
        }

        if (v.key == "update") {
            let data = {
                OriginalCode: currentNode.id,
                Code: currentNode.id,
                X: currentNode.position.x,
                Y: currentNode.position.y,
                Name: currentNode.data.label,
                CandidateMode: currentNode.data.candidateMode,
                PreUserIds: currentNode.data.preUserIds,
                RoleIds: currentNode.data.roleIds,
                DepartmentIds: currentNode.data.departmentIds,
            };

            updateNodeRef.current?.open(data);
        }

        if (v.key == "reject") {
            let data = {
                Code: currentNode.id,
                Name: currentNode.data.label,
                RejectCode: {}
            };

            let result = getRejectInfo(data.Code);
            data.RejectCode = result.rejectCode;

            rejectNodeRef.current?.open(data, result.items);
        }
    }

    const getRejectInfo = (code: any) => {
        let list = getServerData("_temp");
        let nodes: any[] = [];
        let item = list.find((t: any) => t.Code == code);
        let rejectNode = list.find((t) => t.Code == item.RejectNodeCode);
        let rejectCode = rejectNode == null ? null :  rejectNode.Code;

        loopPreNode(nodes, list, code, 0);

        let items = nodes.map((t: any) => { return { label: t.Name, value: t.Code } });
        return { items, rejectCode };
    }

    const loopPreNode = (nodes: any, list: any, code: any, index: number) => {
        let item = list.find((t: any) => t.Code == code);
        if (item != null) {
            let pres = item.PreNodeCodes || [];
            let items = list.filter((t: any) => pres.includes(t.Code)) || [];

            //分两次循环是因为区分层级进行排序
            items.forEach((t: any) => {
                let exist = nodes.find((n: any) => n.Code == t.Code);
                if (exist == null) {
                    if (t.Code != "Start" && t.Code != "End") {
                        index++;
                        nodes.push({ ...t, Index: index });
                    }
                }
            });


            items.forEach((t: any) => {
                loopPreNode(nodes, list, t.Code, index);
            });
        }


    }

    const onConnect = useCallback(
        (params) => {
            setEdges((eds) => addEdge({ ...params, animated: true, type: 'step', logicType: "general", }, eds))
        },
        [],
    );

    const AddNode = (v: any) => {
        let node = nodes.find((t) => t.id == v.Code);
        if (node != null) return false;

        node = {
            id: v.Code,
            type: 'general',
            data: { label: v.Name, candidateMode: v.CandidateMode, preUserIds: v.PreUserIds || [], roleIds: v.RoleIds || [], departmentIds: v.DepartmentIds || [] },
            position: { x: menuPosition.x, y: menuPosition.y },

        };

        let items = [...nodes, node];
        setNodes(items);

        return true;
    }

    const UpdateNode = (v: any) => {
        let items = nodes.filter(item => item.id !== v.OriginalCode);

        let node = items.find((t) => t.id == v.Code);
        if (node != null) return false;

        node = {
            id: v.Code,
            type: 'general',
            data: { label: v.Name, candidateMode: v.CandidateMode, preUserIds: v.PreUserIds || [], roleIds: v.RoleIds || [], departmentIds: v.DepartmentIds || [] },
            position: { x: v.X, y: v.Y },
        };

        items = [...items, node];
        setNodes(items);


        //如果节点没有改变编码则不需要进行路线重定向
        if (v.Code == v.OriginalCode) return true;
        let edge_items = [...edges];
        let sources = edge_items.filter((t) => t.source == v.OriginalCode) || [];
        let targets = edge_items.filter((t) => t.target == v.OriginalCode) || [];

        sources.forEach((t: any) => {
            t.id = t.id.replaceAll(v.OriginalCode, v.Code);
            t.source = t.source.replaceAll(v.OriginalCode, v.Code);
            t.sourceHandle = t.sourceHandle.replaceAll(v.OriginalCode, v.Code);
        });

        targets.forEach((t: any) => {
            t.id = t.id.replaceAll(v.OriginalCode, v.Code);
            t.target = t.target.replaceAll(v.OriginalCode, v.Code);
            t.targetHandle = t.targetHandle.replaceAll(v.OriginalCode, v.Code);
        });

        setEdges([...edge_items]);
        return true;
    }

    const RejectNode = (v: any) => {

        let node = nodes.find((t) => t.id == v.RejectCode);
        if (node == null) return false;

        let items: any[] = edges.filter((item: any) => !(item.source == v.Code && item.logicType == "reject"));
        let edge = getRejectEdge(v.Code, v.RejectCode);
        items = [...items, edge]
        setEdges(items);

        return true;
    }

    const onEdgeClick = (event: any, edge: any) => {
        if (edge.logicType == "reject") return;

    }

    const getPreNodeCodes = (target: any) => {
        let codes = edges.filter((t) => t.target == target && t.logicType == "general").map((t) => t.source);
        return codes;
    }

    const getRejectNodeCode = (source: any) => {
        let edge = edges.find((t) => t.source == source && t.logicType == "reject");
        if (edge == null) return "";
        return edge.target;
    }


    const { run: runSave, loading: loadingSave } = useRequest(SaveFlowConfigNodes, {
        manual: true,
        onSuccess: () => {
            load();
            messageApi.success('提交成功');
        },
        onError: (e: any) => {
            messageApi.error(e);
        },
    });


    const getServerData = (configId: any) => {
        let list: any[] = [];
        nodes.forEach((t: any) => {
            let preCodes = getPreNodeCodes(t.id);
            let rejectCode = getRejectNodeCode(t.id);
            let preUserIds = (t.data.preUserIds || []).map((t: any) => t.value);
            let roleIds = (t.data.roleIds || []).map((t: any) => t.value);
            let departmentIds = (t.data.departmentIds || []).map((t: any) => t.value);
            let item = {
                FlowConfigId: configId,
                Name: t.data.label,
                Code: t.id,
                PositionX: t.position.x,
                PositionY: t.position.y,
                PreNodeCodes: preCodes,
                RejectNodeCode: rejectCode,
                CandidateMode: t.data.candidateMode,
                PreUserIds: preUserIds,
                RoleIds: roleIds,
                DepartmentIds: departmentIds
            };

            if (t.sysId) item.Id = t.sysId;
            list.push(item);
        });

        return list;
    }
    const save = () => {
        //获取页面跳转参数
        let configId = searchParams.get("id");

        if (!configId) {
            messageApi.warning("配置Id异常");
            return;
        }

        let list = getServerData(configId);
        runSave({ Id: configId, Items: list });
    }

    const load = () => {
        let configId = searchParams.get("id");
        let isPublish = searchParams.get("isPublish");
        if (!configId) return;
        setIsPublish(isPublish === "true");

        Http.post("/Flow/FlowConfig/GetFlowConfigNodes", { Id: configId })
            .then((data) => {
                //有数据才赋值
                if (data.length > 0) {
                    let viewData = getFlowViewData(data);
                    setEdges(viewData.edges);
                    setNodes(viewData.nodes);
                }
            }).catch((e) => {
                messageApi.error("加载流程节点失败");
            })
    }

    useEffect(() => {
        load();
        return () => {

        };
    }, []);

    return (
        <>
            {contextHolder}
            <PageContainer
                childrenContentStyle={{ height: 600 }}
                extra={[
                    isPublish ? null : <Button key="save" type="primary" loading={loadingSave} onClick={() => { save(); }}>保存</Button>,
                    <Button key="back" type="default" onClick={() => { history.back(); }}> 返回</Button>,
                ]}>
                <ReactFlowProvider>
                    <ReactFlow
                        ref={containerRef}
                        nodes={nodes}
                        edges={edges}
                        onNodesChange={onNodesChange}
                        onEdgesChange={onEdgesChange}
                        onEdgeClick={onEdgeClick}
                        onConnect={onConnect}
                        onPaneContextMenu={onPaneContextMenu}
                        onNodeContextMenu={onNodeContextMenu}
                        nodeTypes={nodeTypes}
                        connectionLineStyle={{ stroke: "#7dd3fc", strokeWidth: 2 }} // 动态颜色
                        defaultEdgeOptions={{
                            style: {
                                stroke: '#7dd3fc', // 线条颜色
                                strokeWidth: 2,     // 线条宽度
                            },
                            markerEnd: {
                                type: MarkerType.ArrowClosed, // 箭头类型（source端）
                                color: '#7dd3fc',
                            },
                        }}
                    >
                        <Background />
                        <Panel />
                        <Controls />

                        <Dropdown
                            menu={{ items: menuItems, onClick: handleMenuClick }}
                            open={menuVisible}
                            onOpenChange={(visible) => setMenuVisible(visible)}

                            dropdownRender={(menu) => (
                                <div style={{ position: 'absolute', left: `${menuPosition.x}px`, top: `${menuPosition.y}px`, width: "90px" }}>
                                    {menu}
                                </div>
                            )}
                        >
                        </Dropdown>
                        <AddNodeModel ref={addNodeRef} onChange={AddNode} />
                        <UpdateNodeModel ref={updateNodeRef} onChange={UpdateNode} />
                        <RejectNodeModel ref={rejectNodeRef} onChange={RejectNode} />
                    </ReactFlow>

                </ReactFlowProvider >
            </PageContainer>
        </>
    );
};

export default FlowConfigView;
